package org.fuys.coder.infrastructure.repository;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.fuys.coder.infrastructure.model.dto.SubstanceAuthorDTO;
import org.fuys.coder.infrastructure.model.dto.SubstanceDTO;
import org.fuys.coder.infrastructure.model.dto.SubstanceDataStatusDTO;
import org.fuys.coder.infrastructure.model.dto.SubstanceFileDTO;
import org.fuys.coder.common.constants.DBConstants;
import org.fuys.coder.common.constants.FileConstants;
import org.fuys.coder.common.exception.BusinessException;
import org.fuys.coder.common.exception.handler.NeedCallBackException;
import org.fuys.coder.common.holder.CategoryVOHolder;
import org.fuys.coder.domain.audit.model.vo.AuditResultTypeVO;
import org.fuys.coder.domain.category.model.vo.CategoryVO;
import org.fuys.coder.common.entity.PageDTO;
import org.fuys.coder.domain.substance.model.req.admin.SubstancePublishReq;
import org.fuys.coder.domain.substance.model.req.operation.SubstanceDataStatusSelectReq;
import org.fuys.coder.domain.substance.model.vo.SubstanceStatusTypeVO;
import org.fuys.coder.domain.substance.model.vo.SubstanceSupplyVO;
import org.fuys.coder.domain.substance.model.vo.SubstanceVO;
import org.fuys.coder.domain.substance.repository.ISubstanceFileRepository;
import org.fuys.coder.domain.substance.repository.ISubstanceRepository;
import org.fuys.coder.domain.substance.repository.ISubstanceTagRepository;
import org.fuys.coder.infrastructure.dao.SubstanceMapper;
import org.fuys.coder.infrastructure.dao.UserMapper;
import org.fuys.coder.infrastructure.model.po.Substance;
import org.fuys.coder.infrastructure.model.po.SubstanceTag;
import org.fuys.coder.infrastructure.model.po.User;
import org.fuys.coder.infrastructure.util.RedisUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @projectName: fuys-low-coder
 * @package: org.fuys.coder.infrastructure.repository
 * @className: ISubstanceRepositoryImpl
 * @author: WangJing
 * @description: 内容仓储实现类
 * @date: 2024/6/25 18:20
 * @version: 1.0
 */
@Service
public class SubstanceRepositoryImpl extends ServiceImpl<SubstanceMapper, Substance>
        implements ISubstanceRepository {

    @Resource
    private SubstanceMapper substanceMapper;

    @Resource
    private ISubstanceTagRepository substanceTagRepository;

    @Resource
    private ISubstanceFileRepository substanceFileRepository;

    @Resource
    private UserMapper userMapper;

    @Resource
    private RedisUtil redisUtil;

    @Override
    public List<SubstanceDTO> getSubstanceByCategories(List<Integer> categoryList,int curPage,int substanceSize){
        Page<Substance> page=new Page<>(curPage,substanceSize);
        final Page<Substance> substancePage = substanceMapper.selectPage(page, new LambdaQueryWrapper<Substance>()
                .eq(Substance::getAuditStatus, AuditResultTypeVO.PASS.getIndex())
                .eq(Substance::getIsOpen, 1).in(Substance::getCategoryId, categoryList)
                .orderByDesc(Substance::getCreateTime));
        return wrapSubstanceDTO(substancePage);
    }

    @Override
    public void thumbSubstance(Long substanceId, boolean flag) {
        try {
            final Substance substance = substanceMapper.selectOne(new LambdaQueryWrapper<Substance>()
                    .select(Substance::getThumbCount).eq(Substance::getId, substanceId));
            if(ObjectUtils.isEmpty(substance)){
                throw new BusinessException(DBConstants.ERROR_SUBSTANCE_NOT_AVAILABLE);
            }
            substance.setId(substanceId);
            final Integer thumbCount = substance.getThumbCount();
            if(flag){
                substance.setThumbCount(thumbCount+1);
            }else {
                substance.setThumbCount(thumbCount-1);
            }
            substanceMapper.updateById(substance);
        } catch (Exception e) {
            throw new NeedCallBackException(e.getMessage());
        }
    }

    @Override
    public void favoritesSubstance(Long substanceId, boolean flag) {
        try {
            final Substance substance = substanceMapper.selectOne(new LambdaQueryWrapper<Substance>()
                    .select(Substance::getFavoriteCount).eq(Substance::getId, substanceId));
            if(ObjectUtils.isEmpty(substance)){
                throw new BusinessException(DBConstants.ERROR_SUBSTANCE_NOT_AVAILABLE);
            }
            substance.setId(substanceId);
            final Integer favoriteCount = substance.getFavoriteCount();
            if(flag){
                substance.setFavoriteCount(favoriteCount+1);
            }else{
                substance.setFavoriteCount(favoriteCount-1);
            }
            substanceMapper.updateById(substance);
        } catch (Exception e) {
            throw new NeedCallBackException(e.getMessage());
        }
    }

    @Override
    public Long addSubstance(SubstancePublishReq req) {
        try {
            //构造内容对象
            Substance substance=new Substance();
            substance.setTitle(req.getTitle());
            substance.setDescription(req.getIntroduce());
            substance.setCover(req.getCover());
            substance.setAuditStatus(AuditResultTypeVO.ONGOING.getIndex());
            substance.setCategoryId(req.getCategoryId());
            substance.setUserId(req.getAuthorId());
            substanceMapper.insert(substance);
            //构造内容标签对象
            final List<SubstanceTag> collect = req.getLabels().stream().map(label -> {
                SubstanceTag substanceTag = new SubstanceTag();
                substanceTag.setSubstanceId(substance.getId());
                substanceTag.setTagId(label);
                return substanceTag;
            }).collect(Collectors.toList());
            substanceTagRepository.batchInsert(collect);
            substanceFileRepository.batchInsert(substance.getId(), req.getFileMap());
            return substance.getId();
        } catch (Exception e) {
            throw new NeedCallBackException(e.getMessage());
        }
    }

    @Override
    public void setSubstanceStatusById(Long substanceId,AuditResultTypeVO auditResultTypeVO) {
        substanceMapper.update(null,new LambdaUpdateWrapper<Substance>()
                .eq(Substance::getId,substanceId).set(Substance::getAuditStatus,auditResultTypeVO.getIndex()));
    }


    @Override
    public SubstanceSupplyVO getSubstanceSupply(Long substanceId) {
        List<SubstanceFileDTO> list=substanceMapper.selectSubstanceWithFileIds(substanceId);
        SubstanceSupplyVO supplyVO=new SubstanceSupplyVO();
        Map<Integer,Long> fileMap=new HashMap<>();
        list.forEach(item->{
            fileMap.put(item.getFileType(),item.getFileId());
        });
        supplyVO.setFileMap(fileMap);
        return supplyVO;
    }

    @Override
    public SubstanceAuthorDTO getSubstanceAndAuthorIntro(Long substanceId) {
        return substanceMapper.getSubstanceAndAuthorIntro(substanceId);
    }

    @Override
    public List<SubstanceDTO> getSubstanceAfterTime(Date dateBeforeCur, Integer hotSelectNum) {
        return substanceMapper.getWithAfterTime(dateBeforeCur,hotSelectNum);
    }

    @Override
    public List<SubstanceVO> getAuthorSubstancesByOpenFlag(Integer userId, PageDTO pageDTO, boolean showNotOpen) {
        int open=showNotOpen?1:0;
        Page<Substance> substancePage=new Page<>(pageDTO.getCurPage(),pageDTO.getPageNum());
        final Page<Substance> substancePageResult = substanceMapper.selectPage(substancePage, new LambdaQueryWrapper<Substance>().select(Substance::getId, Substance::getCover,
                        Substance::getTitle, Substance::getDescription, Substance::getThumbCount, Substance::getFavoriteCount)
                .eq(Substance::getUserId, userId).eq(Substance::getAuditStatus, AuditResultTypeVO.PASS.getIndex()).ne(open == 0, Substance::getIsOpen, open));
        if(substancePageResult==null){
            return null;
        }
        final List<Substance> substances = substancePageResult.getRecords();
        if(ObjectUtils.isEmpty(substances)){
            return null;
        }
        return substances.stream().map(item -> {
            SubstanceVO substanceVO = new SubstanceVO();
            substanceVO.setCoverId(item.getCover());
            substanceVO.setDescription(item.getDescription());
            substanceVO.setTitle(item.getTitle());
            substanceVO.setThumbCount(item.getThumbCount());
            substanceVO.setFavoriteCount(item.getFavoriteCount());
            substanceVO.setId(item.getId());
            return substanceVO;
        }).collect(Collectors.toList());
    }

    @Override
    public SubstanceDataStatusDTO getSubstanceDataStatus(SubstanceDataStatusSelectReq req) {
        return substanceMapper.getSubstanceDataStatus(req.getSubstanceId(),req.getUserId());
    }

    @Override
    public List<SubstanceDTO> getSubstanceByName(PageDTO pageDTO, String keywords) {
        //这里还是决定进行多次查询 如果使用like 匹配 脑测不如多次分批查询
        Page<Substance> page=new Page<>(pageDTO.getCurPage(),pageDTO.getPageNum());
        //原本是想让描述也参与匹配 但是使用or的话 对性能影响较大 故不加了 加上需要设置and条件组
        //.like(Substance::getDescription, keywords)
        final Page<Substance> substancePage = substanceMapper.selectPage(page, new LambdaQueryWrapper<Substance>()
                .select(Substance::getTitle, Substance::getDescription, Substance::getId, Substance::getUserId
                        , Substance::getThumbCount, Substance::getFavoriteCount, Substance::getCategoryId, Substance::getCover)
                .eq(Substance::getIsOpen,1).eq(Substance::getAuditStatus,AuditResultTypeVO.PASS.getIndex())
                .like(Substance::getTitle, keywords));
        return wrapSubstanceDTO(substancePage);
    }

    @Nullable
    private List<SubstanceDTO> wrapSubstanceDTO(Page<Substance> substancePage) {
        List<Substance> records =null;
        if(ObjectUtils.isEmpty(substancePage)){
            return null;
        }
        records = substancePage.getRecords();
        if(ObjectUtils.isEmpty(records)){
            return null;
        }
        List<SubstanceDTO> resultList=new ArrayList<>();
        final Map<Integer, User> userMap = userMapper.selectList(new LambdaQueryWrapper<User>().select(User::getId,User::getNickName, User::getAvatar, User::getIntroduce,
                        User::getFollowerCount).in(User::getId, records.stream().map(Substance::getUserId).collect(Collectors.toList()))).
                stream().collect(Collectors.toMap(User::getId, user -> user));
        records.forEach(item->{
            final Integer userId = item.getUserId();
            final Integer categoryId = item.getCategoryId();
            final User user = userMap.get(userId);
//            final Category category = categoryMap.get(categoryId);

            SubstanceDTO substanceDTO=new SubstanceDTO();
            substanceDTO.setCategoryId(item.getCategoryId());
            substanceDTO.setCoverId(item.getCover());
            substanceDTO.setId(item.getId());
            substanceDTO.setTitle(item.getTitle());
            substanceDTO.setDescription(item.getDescription());
            substanceDTO.setFavoriteCount(item.getFavoriteCount());
            substanceDTO.setThumbCount(item.getThumbCount());
            substanceDTO.setCreateTime(item.getCreateTime());
            substanceDTO.setUserId(userId);
            substanceDTO.setNickName(user==null?"用户已注销":user.getNickName());
            substanceDTO.setAvatar(user==null? Long.valueOf(FileConstants.DEFAULT_AVATAR_ID):Long.valueOf(user.getAvatar()));
            if(!ObjectUtils.isEmpty(categoryId)){
                final CategoryVO byId = CategoryVOHolder.getById(categoryId);
                substanceDTO.setCategoryName(byId.getName());
                substanceDTO.setCategoryDesc(byId.getDescription());
            }
            //            if(!ObjectUtils.isEmpty(category)){
//                substanceDTO.setCategoryName(category.getCategoryName());
//                substanceDTO.setCategoryDesc(category.getCategoryDesc());
//            }
            resultList.add(substanceDTO);
        });
        return resultList;
    }

    @Override
    public SubstanceDTO getSubstanceById(Long id) {
        final Substance substance = substanceMapper.selectOne(new LambdaQueryWrapper<Substance>()
                .select(Substance::getTitle, Substance::getDescription, Substance::getId, Substance::getUserId
                        , Substance::getThumbCount, Substance::getFavoriteCount, Substance::getCategoryId, Substance::getCover)
                .eq(Substance::getId, id));
        if(substance==null){
            return null;
        }
        return transferPOToDTO(substance);
    }

    @Override
    public void modifySubstanceStatusById(Long id, Integer status, Integer type) {
        Substance substance=new Substance();
        substance.setId(id);
        if(SubstanceStatusTypeVO.AUDIT.equals(type)){
            substance.setAuditStatus(status);
        }else if(SubstanceStatusTypeVO.OPEN.equals(type)){
            substance.setIsOpen(status);
        }
        substanceMapper.updateById(substance);
    }

    @Override
    public List<SubstanceDTO> getSubstances(List<Long> id) {
        final List<Substance> substances = substanceMapper.selectList(new LambdaQueryWrapper<Substance>()
                .select(Substance::getTitle, Substance::getDescription, Substance::getId, Substance::getUserId
                        , Substance::getThumbCount, Substance::getFavoriteCount, Substance::getCategoryId, Substance::getCover)
                .in(Substance::getId, id));
        if(ObjectUtils.isEmpty(substances)){
            return null;
        }
        return substances.stream().map(this::transferPOToDTO).collect(Collectors.toList());
    }

    @NotNull
    private SubstanceDTO transferPOToDTO(Substance substance) {
        SubstanceDTO substanceDTO=new SubstanceDTO();
        substanceDTO.setCategoryId(substance.getCategoryId());
        substanceDTO.setUserId(substance.getUserId());
        substanceDTO.setTitle(substance.getTitle());
        substanceDTO.setId(substance.getId());
        substanceDTO.setCoverId(substance.getCover());
        substanceDTO.setThumbCount(substance.getThumbCount());
        substanceDTO.setFavoriteCount(substance.getFavoriteCount());
        substanceDTO.setDescription(substance.getDescription());
        return substanceDTO;
    }

    @Override
    public Set<SubstanceVO> getCacheSubstance(String key,Integer count) {
        final Set<Object> objects = redisUtil.getRedisTemplate().opsForSet().distinctRandomMembers(key, count);
        if(ObjectUtils.isEmpty(objects)){
            return null;
        }
        return objects.stream().map(item-> {
            return (SubstanceVO)item;
        }).collect(Collectors.toSet());
    }
}
